﻿#include <iostream>
#include <opencv2\opencv.hpp>

//参考链接https://blog.csdn.net/mvtechnology/article/details/8146858
//参考链接https://www.cnblogs.com/phoenixdsg/p/8280362.html

using namespace cv;
using namespace std;

Mat make_draw_hist_mat(const Mat& hist, Size imgSize)
{
    Mat histImg(imgSize, CV_8UC3);
    int Padding = 10;
    int W = imgSize.width - 2 * Padding;
    int H = imgSize.height - 2 * Padding;
    double _max;
    minMaxLoc(hist, NULL, &_max);
    double Per = (double)H / _max;
    const Point Orig(Padding, imgSize.height - Padding);
    int bin = 2;// W / (hist.rows + 2);

    //箭头(不画了)
    //arrowedLine(InputOutputArray img, Point pt1, Point pt2, const Scalar & color, int thickness = 1, int lineType = 8, int shift = 0, double tipLength = 0.1)
    //arrowedLine(histImg, Point(Orig.x + bin, Orig.y), Point(Orig.x + hist.rows * bin, Orig.y), Scalar::all(0), 2, LINE_4);
    //arrowedLine(histImg, Point(Orig.x + bin, Orig.y), Point(Orig.x + bin, Orig.y - H), Scalar::all(0), 2, LINE_4);

    //画方柱
    int line_color_idx = 0;
    for (int i = 1; i <= hist.rows; i++)
    {
        Point pBottom(Orig.x + i * (bin+1), Orig.y);
        Point pTop(pBottom.x, pBottom.y - Per * hist.at<float>(i - 1));
        if (line_color_idx == 0)
        {
            line(histImg, pBottom, pTop, Scalar(255, 0, 0), bin);
        }
        else if (line_color_idx == 1)
        {
            line(histImg, pBottom, pTop, Scalar(0, 0, 255), bin);
        }
        else if (line_color_idx == 2)
        {
            line(histImg, pBottom, pTop, Scalar(0, 255, 0), bin);
        }
        line_color_idx++;
        if (line_color_idx >= 3)
        {
            line_color_idx = 0;
        }
        
    }

    //画 3 条红线标明区域
    line(histImg, Point(Orig.x + bin, Orig.y - H), Point(Orig.x + hist.rows * (bin + 1), Orig.y - H), Scalar(0, 0, 255), 1);
    line(histImg, Point(Orig.x + bin, Orig.y), Point(Orig.x + bin, Orig.y - H), Scalar(0, 0, 255), 1);
    line(histImg, Point(Orig.x + hist.rows * (bin + 1), Orig.y), Point(Orig.x + hist.rows * (bin + 1), Orig.y - H), Scalar(0, 0, 255), 1);
    


    return histImg;
}

int main()
{
    //Mat img = imread("lena.jpg", CV_LOAD_IMAGE_GRAYSCALE);
    Mat img = imread("pic2.png", CV_LOAD_IMAGE_GRAYSCALE);

    Mat* arrays = &img;
    int narrays = 1;
    int channels[] = { 0 };
    InputArray mask = noArray();
    Mat hist;
    int dims = 1;
    int histSize[] = { 256 };
    float hranges[] = { 0.0, 255.0 };
    const float* ranges[] = { hranges };

    cout << img.channels();

    //函数calcHist可以计算一幅或多幅图像的直方图。在元组中增量一个直方图的时候，就是从输入图像组中的原位置提取一幅图像，并计算出它的直方图，并添加到元组中。
    //当参数dims > 1时，输出矩阵Hist是二维矩阵。

    //images –  源图像数组，它们有同样的位深CV_8U或 CV_32F ，同样的尺寸；图像阵列中的每一个图像都可以有任意多个通道；

    //nimages –  源图像的数目。

    //channels – 维度通道序列，第一幅图像的通道标号从0~image[0].channels() - 1。Image[0]表示图像数组中的第一幅图像，channels（）表示该图像的通道数量。
    //            同理，图像阵列中的第二幅图像，通道标号从image[0].channerls()开始，到image[1].channels() - 1为止；
    //            第三、四幅图像的通道标号顺序依此类推；
    //            也就是说图像阵列中的所有图像的通道根据图像排列顺序，排成一个通道队列。

    //mask –     可选择的mask。如果该矩阵不空的话，它必须是一个8 - bit的矩阵，与images[i]同尺寸。在图像中，只有被mask覆盖的区域的像素才参与直方图统计。如果这个参数想用默认值，输入Mat()就可以了。

    //hist –     输出直方图， 它是一个稠密或稀疏矩阵，具有dims个维度；

    //dims –     直方图的维度，一定是正值， CV_MAX_DIMS（当前OpenCV版本是32个）；

    //histSize – 数组，即histSize[i]表示第i个维度上bin的个数；这里的维度可以理解为通道。

    //ranges –   当uniform = true时，ranges是多个二元数组组成的数组；当uniform = false时，ranges是多元数组组成的数组。
    //              当在每个维度（或通道）上每个直方条等宽时，即uniform = true时，灰度值的有效统计范围的下界用L0表示，上界用UhistSize[i] - 1表示，角标中的i表示第i个维度（或通道），上下界值可以表示为hrange[i] = { L0, UhistSize[i] - 1 }, 在统计时， L0和UhistSize[i] - 1不在统计范围内。而ranges = { hrange[0], hrange[1], …… , hrange[dims] }。ranges的元素个数由参数dims决定。
    //              其中，L0表示在该通道上第0个直方条（bin）的下边界，UhistSize[i] - 1表示最后一个直方条histSize[i] - 1的上边界。在该维度上直方条的个数为histSize[i]，如hrange[0] = { L0, UhistSize[0] }, hrange[1] = { L1, UhistSize[1] }, hrange[2] = { L2, UhistSize[2] }, ……, hrange[dims] = { L0, UhistSize[0] }。
    //              当uniform = false时，ranges中的每个元素ranges[i]都是一个多元数组，元素个数为histSize[i] + 1，它们分别是：L0, U0 = L1, U1 = L2, ……, UhistSize[i] - 2, LhistSize[i] - 1, UhistSize[i] - 1 。所以，ranges[i] = { L0 , L1, L2, …… , LhistSize[i] - 1 ,UhistSize[i] - 1 }

    //uniform –  标识，用于说明直方条bin是否是均匀等宽的。

    //accumulate – 累积标识。如果该项设置，当直方图重新分配时，直方图在开始清零。这个特征可以使你通过几幅图像，累积计算一个简单的直方图，或者及时更新直方图。


    calcHist(arrays, narrays, channels, mask, hist, dims, histSize, ranges);

    Mat histImg = make_draw_hist_mat(hist, Size(800, 420));
    imshow("pic2", img);
    imshow("pic2_histogram", histImg);
    waitKey();
}

